package com.minimalist.common.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类
 * 基于hutool的JWT实现
 *
 * @author minimalist
 */
public class JwtUtil{

    private static final Logger log = LoggerFactory.getLogger(JwtUtil.class);

    /**
     * 默认密钥
     */
    private static final String DEFAULT_SECRET = "minimalist-saas-jwt-secret-key-2024";

    /**
     * 默认过期时间（7天，单位：毫秒）
     */
    private static final long DEFAULT_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000L;

    /**
     * Token前缀
     */
    public static final String TOKEN_PREFIX = "Bearer ";

    /**
     * 用户ID在JWT中的键名
     */
    public static final String USER_ID_KEY = "userId";

    /**
     * 用户名在JWT中的键名
     */
    public static final String USERNAME_KEY = "username";

    /**
     * 角色在JWT中的键名
     */
    public static final String ROLES_KEY = "roles";

    /**
     * 生成JWT Token
     *
     * @param userId   用户ID
     * @param username 用户名
     * @return JWT Token
     */
    public static String generateToken(Long userId, String username){
        return generateToken(userId, username, null, DEFAULT_EXPIRE_TIME);
    }

    /**
     * 生成JWT Token
     *
     * @param userId   用户ID
     * @param username 用户名
     * @param roles    角色列表
     * @return JWT Token
     */
    public static String generateToken(Long userId, String username, String roles){
        return generateToken(userId, username, roles, DEFAULT_EXPIRE_TIME);
    }

    /**
     * 生成JWT Token
     *
     * @param userId     用户ID
     * @param username   用户名
     * @param roles      角色列表
     * @param expireTime 过期时间（毫秒）
     * @return JWT Token
     */
    public static String generateToken(Long userId, String username, String roles, long expireTime){
        return generateToken(userId, username, roles, expireTime, DEFAULT_SECRET);
    }

    /**
     * 生成JWT Token
     *
     * @param userId     用户ID
     * @param username   用户名
     * @param roles      角色列表
     * @param expireTime 过期时间（毫秒）
     * @param secret     密钥
     * @return JWT Token
     */
    public static String generateToken(Long userId, String username, String roles, long expireTime, String secret){
        try{
            Date now = new Date();
            Date expireDate = new Date(now.getTime() + expireTime);

            Map<String, Object> payload = new HashMap<>();
            payload.put(JWTPayload.ISSUED_AT, now);
            payload.put(JWTPayload.EXPIRES_AT, expireDate);
            payload.put(JWTPayload.NOT_BEFORE, now);
            payload.put(USER_ID_KEY, userId);
            payload.put(USERNAME_KEY, username);

            if (StrUtil.isNotBlank(roles)){
                payload.put(ROLES_KEY, roles);
            }

            return JWTUtil.createToken(payload, secret.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e){
            log.error("生成JWT Token失败", e);
            throw new RuntimeException("生成JWT Token失败", e);
        }
    }

    /**
     * 验证JWT Token
     *
     * @param token JWT Token
     * @return 是否有效
     */
    public static boolean verifyToken(String token){
        return verifyToken(token, DEFAULT_SECRET);
    }

    /**
     * 验证JWT Token
     *
     * @param token  JWT Token
     * @param secret 密钥
     * @return 是否有效
     */
    public static boolean verifyToken(String token, String secret){
        try{
            if (StrUtil.isBlank(token)){
                return false;
            }

            // 移除Bearer前缀
            token = removeTokenPrefix(token);

            return JWTUtil.verify(token, secret.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e){
            log.warn("验证JWT Token失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 解析JWT Token获取载荷
     *
     * @param token JWT Token
     * @return 载荷信息
     */
    public static JWT parseToken(String token){
        return parseToken(token, DEFAULT_SECRET);
    }

    /**
     * 解析JWT Token获取载荷
     *
     * @param token  JWT Token
     * @param secret 密钥
     * @return 载荷信息
     */
    public static JWT parseToken(String token, String secret){
        try{
            if (StrUtil.isBlank(token)){
                return null;
            }

            // 移除Bearer前缀
            token = removeTokenPrefix(token);

            // 先验证token
            if (!JWTUtil.verify(token, secret.getBytes(StandardCharsets.UTF_8))){
                return null;
            }

            return JWTUtil.parseToken(token);
        } catch (Exception e){
            log.warn("解析JWT Token失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 从Token中获取用户ID
     *
     * @param token JWT Token
     * @return 用户ID
     */
    public static Long getUserId(String token){
        JWT jwt = parseToken(token);
        if (jwt == null){
            return null;
        }

        Object userId = jwt.getPayload(USER_ID_KEY);
        if (userId == null){
            return null;
        }

        if (userId instanceof Number){
            return ((Number) userId).longValue();
        }

        try{
            return Long.parseLong(userId.toString());
        } catch (NumberFormatException e){
            log.warn("解析用户ID失败: {}", userId);
            return null;
        }
    }

    /**
     * 从Token中获取用户名
     *
     * @param token JWT Token
     * @return 用户名
     */
    public static String getUsername(String token){
        JWT jwt = parseToken(token);
        if (jwt == null){
            return null;
        }

        Object username = jwt.getPayload(USERNAME_KEY);
        return username != null ? username.toString() : null;
    }

    /**
     * 从Token中获取角色
     *
     * @param token JWT Token
     * @return 角色列表
     */
    public static String getRoles(String token){
        JWT jwt = parseToken(token);
        if (jwt == null){
            return null;
        }

        Object roles = jwt.getPayload(ROLES_KEY);
        return roles != null ? roles.toString() : null;
    }

    /**
     * 检查Token是否过期
     *
     * @param token JWT Token
     * @return 是否过期
     */
    public static boolean isTokenExpired(String token){
        JWT jwt = parseToken(token);
        if (jwt == null){
            return true;
        }

        Object exp = jwt.getPayload(JWTPayload.EXPIRES_AT);
        if (exp == null){
            return true;
        }

        Date expireDate;
        if (exp instanceof Date){
            expireDate = (Date) exp;
        } else if (exp instanceof Number){
            expireDate = new Date(((Number) exp).longValue() * 1000);
        } else{
            try{
                expireDate = DateUtil.parse(exp.toString());
            } catch (Exception e){
                log.warn("解析过期时间失败: {}", exp);
                return true;
            }
        }

        return expireDate.before(new Date());
    }

    /**
     * 获取Token的剩余有效时间（秒）
     *
     * @param token JWT Token
     * @return 剩余有效时间（秒），如果已过期或解析失败返回0
     */
    public static long getTokenRemainingTime(String token){
        JWT jwt = parseToken(token);
        if (jwt == null){
            return 0;
        }

        Object exp = jwt.getPayload(JWTPayload.EXPIRES_AT);
        if (exp == null){
            return 0;
        }

        Date expireDate;
        if (exp instanceof Date){
            expireDate = (Date) exp;
        } else if (exp instanceof Number){
            expireDate = new Date(((Number) exp).longValue() * 1000);
        } else{
            try{
                expireDate = DateUtil.parse(exp.toString());
            } catch (Exception e){
                log.warn("解析过期时间失败: {}", exp);
                return 0;
            }
        }

        long remaining = expireDate.getTime() - System.currentTimeMillis();
        return Math.max(0, remaining / 1000);
    }

    /**
     * 刷新Token（生成新的Token）
     *
     * @param token 原Token
     * @return 新Token
     */
    public static String refreshToken(String token){
        return refreshToken(token, DEFAULT_EXPIRE_TIME);
    }

    /**
     * 刷新Token（生成新的Token）
     *
     * @param token      原Token
     * @param expireTime 新的过期时间（毫秒）
     * @return 新Token
     */
    public static String refreshToken(String token, long expireTime){
        JWT jwt = parseToken(token);
        if (jwt == null){
            return null;
        }

        Long userId = getUserId(token);
        String username = getUsername(token);
        String roles = getRoles(token);

        if (userId == null || StrUtil.isBlank(username)){
            return null;
        }

        return generateToken(userId, username, roles, expireTime);
    }

    /**
     * 移除Token前缀
     *
     * @param token 原Token
     * @return 去除前缀后的Token
     */
    private static String removeTokenPrefix(String token){
        if (StrUtil.isBlank(token)){
            return token;
        }

        if (token.startsWith(TOKEN_PREFIX)){
            return token.substring(TOKEN_PREFIX.length());
        }

        return token;
    }

    /**
     * 添加Token前缀
     *
     * @param token 原Token
     * @return 添加前缀后的Token
     */
    public static String addTokenPrefix(String token){
        if (StrUtil.isBlank(token)){
            return token;
        }

        if (token.startsWith(TOKEN_PREFIX)){
            return token;
        }

        return TOKEN_PREFIX + token;
    }
}
